﻿using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Reflection;
using INI;

using FullMotion.LiveForSpeed.InSim;
using FullMotion.LiveForSpeed.InSim.Enums;
using FullMotion.LiveForSpeed.InSim.Events;
using FullMotion.LiveForSpeed.InSim.Exceptions;

namespace StormCup_Qual
{
    class Program
    {
        private string ip;
        private int port;
        private string adminPass;
        private char prefix;

        private static string ProgramTitle;
        private string ProgramVersion;
        private string ProgramCompany;
        private string hostname;

        private InSimHandler handler = new InSimHandler();
        private Hashtable racerDatabase = new Hashtable();

        private Queue Qqueue = new Queue();
        private int QualificationLaps = 1;
        private int QualifyingMode;

        private string qualstarttext;
        private string qualendtext;
        private string pitlaneentrytext;

        private int helplines;
        private string helpfile;

        private LfsButton[] help;
        private LfsButton OK = new LfsButton(50);

        static void Main(string[] args)
        {
            try
            {
                Program p = new Program();
                p.Config();
                p.Setup();
                p.Start();
                p.Threading();
            }
            catch (InSimHandlerException.ConnectionFailed connfail)
            {
                Console.WriteLine(ProgramTitle + " could not connect. Please check your IP, Port");
                Console.WriteLine("and Admin Pass settings in the config.ini file");
                Console.WriteLine(Environment.NewLine);
                Console.WriteLine(connfail.Message);
                Console.WriteLine(Environment.NewLine);
                Console.WriteLine(connfail.StackTrace);
                Console.ReadLine();
                Environment.Exit(0);
            }
            catch (InSimHandlerException.NotConnected NC)
            {
                Console.WriteLine(NC.Message);
                Console.WriteLine(Environment.NewLine);
                Console.WriteLine(NC.StackTrace);
                Console.ReadLine();
                Environment.Exit(0);
            }
        }

        public void Config()
        {
            IniFile ini = new IniFile(".\\config.ini");
            ip = ini.IniReadValue("server", "ip");
            port = Convert.ToInt32(ini.IniReadValue("server", "port"));
            adminPass = ini.IniReadValue("server", "admin pass");
            prefix = Convert.ToChar(ini.IniReadValue("server", "insim prefix"));
            QualificationLaps = Convert.ToInt32(ini.IniReadValue("server", "laps"));
            QualifyingMode = Convert.ToInt16(ini.IniReadValue("server", "mode"));

            if ((QualifyingMode > 6) | (QualifyingMode < 0))
            {
                Console.WriteLine("Invalid Qualifying Mode selected");
                Console.WriteLine("Please change Qualifying Mode in config.ini appropriately");
                Console.WriteLine("Press any key to exit");
                Console.ReadLine();
                Environment.Exit(0);
            }

            qualstarttext = ini.IniReadValue("text", "qual start");
            qualendtext = ini.IniReadValue("text", "qual end");
            pitlaneentrytext = ini.IniReadValue("text", "pitlane entry");

            helplines = Convert.ToInt16(ini.IniReadValue("help", "number of lines"));
            help = new LfsButton[helplines];
            helpfile = ini.IniReadValue("help", "file location");
        }

        public void Setup()
        {
            byte buttoncount = 0;

            try
            {
                StreamReader re = File.OpenText(helpfile);

                for (int i = 0; i < helplines; i++)
                {
                    help[i] = new LfsButton(buttoncount);
                    help[i].Width = 160;
                    help[i].Left = 20;
                    help[i].Top = (byte)(10 + (i * 5));
                    help[i].Height = 5;
                    help[i].TextAlignment = ButtonTextAlignment.Left;
                    help[i].Color = ButtonColor.Dark;
                    help[i].TextColor = ButtonTextColor.SelectedText;

                    string input = "";
                    if ((input = re.ReadLine()) != null)
                    {
                        if (input.StartsWith("[H]"))
                        {
                            help[i].TextColor = ButtonTextColor.TitleColor;
                            input = input.Substring(3);
                        }
                        help[i].Text = input;
                    }
                    buttoncount++;
                }
                re.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.ReadLine();
                Environment.Exit(0);
            }

            OK.Text = "OK";
            OK.Left = 20;
            OK.TextAlignment = ButtonTextAlignment.Center;
            OK.Height = 10;
            OK.Color = ButtonColor.Light;
            OK.TextColor = ButtonTextColor.Ok;
            OK.Width = 20;
            OK.Top = (byte)(10 + (help.Length * 5));
            OK.Clickable = true;

            handler.Configuration.LFSHost = ip;
            handler.Configuration.LFSHostPort = port;
            handler.Configuration.AdminPass = adminPass;
            handler.Configuration.UseTCP = true;
            handler.Configuration.ProgramName = "Auto Qual";
            handler.Configuration.Prefix = prefix;

            handler.ButtonClick += new FullMotion.LiveForSpeed.InSim.EventHandlers.ButtonClickHandler(handler_ButtonClick);
            handler.RaceTrackPlayerSplitTime += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerSplitTimeHandler(handler_RaceTrackPlayerSplitTime);
            handler.LFSMessage += new FullMotion.LiveForSpeed.InSim.EventHandlers.MessageEventHandler(handler_LFSMessage);
            handler.LFSMultiplayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.MultiplayerEventHandler(handler_LFSMultiplayer);
            handler.RaceTrackConnection += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackNewConnectionHandler(handler_RaceTrackConnection);
            //handler.RaceTrackConnectionLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackConnectionLeaveHandler(handler_RaceTrackConnectionLeave);
            handler.RaceTrackPlayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerHandler(handler_RaceTrackPlayer);
            handler.RaceTrackPlayerLap += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLapHandler(handler_RaceTrackPlayerLap);
            handler.RaceTrackPlayerLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLeaveHandler(handler_RaceTrackPlayerLeave);
            handler.RaceTrackPlayerPits += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitsHandler(handler_RaceTrackPlayerPits);
            handler.RaceTrackReorder += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackReorderHandler(handler_RaceTrackReorder);
            handler.RaceTrackRaceStart += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackRaceStartHandler(handler_RaceTrackRaceStart);
            handler.RaceTrackPlayerPitlane += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitlaneHandler(handler_RaceTrackPlayerPitlane);
            handler.RaceTrackPlayerRename += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerRenameHandler(handler_RaceTrackPlayerRename);
        }

        void handler_LFSMultiplayer(InSimHandler sender, Multiplayer e)
        {
            if (hostname != e.HostName)
            {
                hostname = e.HostName;
                Console.Title += " Connected to:" + hostname;
                Console.WriteLine("Connected to: " + hostname);
            }
        }

        void handler_ButtonClick(InSimHandler sender, ButtonClick e)
        {
            if (e.ButtonId == 50)
            {
                for (int i = 0; i <= helplines; i++)
                {
                    handler.DeleteButton((byte)i, e.ConnectionId);
                }
                handler.DeleteButton(OK, e.ConnectionId);
            }
        }

        void handler_RaceTrackPlayerSplitTime(InSimHandler sender, RaceTrackPlayerSplitTime e)
        {
            lfsracer racer = null;
            racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
            switch (e.Split)
            {
                case 1:

                    if (QualifyingMode == 1)
                        SendNewQualifier(racer);

                    break;
                case 2:

                    if (QualifyingMode == 2)
                        SendNewQualifier(racer);

                    break;
                case 3:

                    if (QualifyingMode == 3)
                        SendNewQualifier(racer);

                    break;
            }
        }

        void handler_RaceTrackPlayerRename(InSimHandler sender, RaceTrackPlayerRename e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];
                racer.playername = e.PlayerName;
            }
        }

        void handler_RaceTrackPlayerPitlane(InSimHandler sender, RaceTrackPlayerPitlane e)
        {
            lfsracer racer = null;
            racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
            switch (e.EventType)
            {
                case Pitlane.DriveThrough:
                    break;
                case Pitlane.Enter:
                    
                    if (QualifyingMode == 6)
                        SendNewQualifier(racer);

                    if (racer != null)
                    {
                        if (racer.Status == lfsracer.RacerQStatus.inlap)
                        {
                            racer.Status = lfsracer.RacerQStatus.finished;
                            handler.SendMessageToConnection(pitlaneentrytext, racer.connectionid);
                        }
                    }
                    break;
                case Pitlane.Exit:

                    if (QualifyingMode == 0)
                    {
                        SendNewQualifier(racer);
                        //AdminCall(racer.playername + TextColor.White + " left the pitlane.");
                    }

                    #region old
                    //if (racer != null)
                    //{
                    //    if (Qqueue.Count > 0)
                    //    {
                    //        lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                    //        if (Qualifier != null)
                    //        {
                    //            if (racer.username.Equals(Qualifier.username))
                    //            {
                    //                // We got the first one in the Queue :)
                    //                racer.Status = lfsracer.RacerQStatus.outlap;
                    //                //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                    //                this.AdminCall(racer.playername + TextColor.White + " left the pitlane.");
                    //                Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                    //                if (Qqueue.Count == 0)
                    //                {
                    //                    //handler.SendMessage(racer.username + " is the last one");
                    //                    this.AdminCall(racer.playername + TextColor.White + " is the last one");
                    //                }

                    //                // Check again, if the queue is empty!
                    //                if (Qqueue.Count > 0)
                    //                {
                    //                    lfsracer nextracer = (lfsracer)Qqueue.Peek();
                    //                    if (nextracer != null)
                    //                    {
                    //                        //handler.SendMessage(nextracer.username + " your next!");
                    //                        handler.SendMessageToConnection(nextracer.playername + TextColor.White + ", Start your qualification run", nextracer.connectionid);
                    //                        this.AdminCall(nextracer.playername + TextColor.White + " is next!");
                    //                    }
                    //                }
                    //            }
                    //        }
                    //        else
                    //        {
                    //            Qqueue.Dequeue();
                    //            if (Qqueue.Count > 0)
                    //            {
                    //                lfsracer NextQualifier = (lfsracer)Qqueue.Peek();
                    //                if (NextQualifier != null)
                    //                {
                    //                    if (racer.username.Equals(NextQualifier.username))
                    //                    {
                    //                        // We got the first one in the Queue :)
                    //                        racer.Status = lfsracer.RacerQStatus.outlap;
                    //                        //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                    //                        this.AdminCall(racer.playername + TextColor.White + " left the pitlane.");
                    //                        Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                    //                        if (Qqueue.Count == 0)
                    //                        {
                    //                            //handler.SendMessage(racer.username + " is the last one");
                    //                            this.AdminCall(racer.playername + TextColor.White + " is the last one");
                    //                        }

                    //                        // Check again, if the queue is empty!
                    //                        if (Qqueue.Count > 0)
                    //                        {
                    //                            lfsracer nextracer = (lfsracer)Qqueue.Peek();
                    //                            if (nextracer != null)
                    //                            {
                    //                                //handler.SendMessage(nextracer.username + " your next!");
                    //                                handler.SendMessageToConnection(nextracer.playername + TextColor.White + ", Start your qualification run", nextracer.connectionid);
                    //                                this.AdminCall(nextracer.playername + TextColor.White + " is next!");
                    //                            }
                    //                        }
                    //                    }
                    //                }
                    //            }
                    //        }
                    //    }
                    //} 
                    #endregion

                    break;
                case Pitlane.NoPurpose:
                    break;
                case Pitlane.Num:
                    break;
                case Pitlane.StopGo:
                    break;
                default:
                    break;
            }
        }

        void SendNewQualifier(lfsracer racer)
        {
            if (racer != null)
            {
                if (Qqueue.Count > 0)
                {
                    lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                    if (Qualifier != null)
                    {
                        if (racer.username.Equals(Qualifier.username))
                        {
                            // We got the first one in the Queue :)
                            racer.Status = lfsracer.RacerQStatus.outlap;
                            //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                            //AdminCall(racer.playername + TextColor.White + " left the pitlane.");
                            Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                            if (Qqueue.Count == 0)
                            {
                                //handler.SendMessage(racer.username + " is the last one");
                                this.AdminCall(racer.playername + TextColor.White + " is the last one");
                            }

                            // Check again, if the queue is empty!
                            if (Qqueue.Count > 0)
                            {
                                lfsracer nextracer = (lfsracer)Qqueue.Peek();
                                if (nextracer != null)
                                {
                                    //handler.SendMessage(nextracer.username + " your next!");
                                    handler.SendMessageToConnection(nextracer.playername + TextColor.White + ", Start your qualification run", nextracer.connectionid);
                                    this.AdminCall(nextracer.playername + TextColor.White + " is next!");
                                }
                            }
                        }
                    }
                    else
                    {
                        Qqueue.Dequeue();
                        if (Qqueue.Count > 0)
                        {
                            lfsracer NextQualifier = (lfsracer)Qqueue.Peek();
                            if (NextQualifier != null)
                            {
                                if (racer.username.Equals(NextQualifier.username))
                                {
                                    // We got the first one in the Queue :)
                                    racer.Status = lfsracer.RacerQStatus.outlap;
                                    //handler.SendMessage("Racer Left the pitlane, Next one lineup :) ");
                                    //AdminCall(racer.playername + TextColor.White + " left the pitlane.");
                                    Qqueue.Dequeue(); // It returns a racer class, but we don't need any info from it :)

                                    if (Qqueue.Count == 0)
                                    {
                                        //handler.SendMessage(racer.username + " is the last one");
                                        this.AdminCall(racer.playername + TextColor.White + " is the last one");
                                    }

                                    // Check again, if the queue is empty!
                                    if (Qqueue.Count > 0)
                                    {
                                        lfsracer nextracer = (lfsracer)Qqueue.Peek();
                                        if (nextracer != null)
                                        {
                                            //handler.SendMessage(nextracer.username + " your next!");
                                            handler.SendMessageToConnection(nextracer.playername + TextColor.White + ", Start your qualification run", nextracer.connectionid);
                                            this.AdminCall(nextracer.playername + TextColor.White + " is next!");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        void handler_LFSMessage(InSimHandler sender, Message e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];

                if (e.UserType == UserType.Prefix)
                {
                    if (e.MessageText.ToLower().Contains("help"))
                    {
                        handler.ShowButton(OK, e.ConnectionId);

                        for (int i = 0; i < this.helplines; i++)
                        {
                            handler.ShowButton(help[i], e.ConnectionId);
                        }
                    }

                    if (e.MessageText.Contains("qlist".ToLower()))
                    {
                        if (Qqueue.Count > 0)
                        {
                            if (Qqueue.Count == 1)
                                handler.SendMessageToConnection(TextColor.White + Qqueue.Count + " qualifier left", e.ConnectionId);
                            else
                                handler.SendMessageToConnection(TextColor.White + Qqueue.Count + " qualifiers left", e.ConnectionId);
                            handler.SendMessageToConnection(TextColor.White + "Queue listing-Next 10:", e.ConnectionId);
                            int position = 1;
                            lock (Qqueue)
                            {
                                foreach (lfsracer lfsracer in Qqueue)
                                {
                                    handler.SendMessageToConnection(TextColor.White + position++ + ". " + lfsracer.playername, e.ConnectionId);
                                    if (position > 10)
                                        break;
                                }
                            }
                        }
                        else
                        {
                            handler.SendMessageToConnection(TextColor.White + "Queue list is empty, 0 qualifiers", e.ConnectionId);
                        }
                    }

                    // An Admin command! to add a late racer to the bottom of the list :P
                    if ((e.MessageText.Contains("qadd".ToLower())) && (racer.isadmin))
                    {
                        string username = e.MessageText;
                        username = e.MessageText.Substring(username.IndexOf("qadd ") + 5);

                        lfsracer lateQracer = (lfsracer)racerDatabase[getConnectionid(username)];
                        if (lateQracer != null)
                        {
                            if (lateQracer.Status == lfsracer.RacerQStatus.finished)
                                lateQracer.Status = lfsracer.RacerQStatus.spectated;
                            Qqueue.Enqueue(lateQracer);
                            AdminCall(lateQracer.playername + TextColor.White + " added to the Qualification List");
                        }
                        else
                        {
                            AdminCall("Wrong username");
                        }
                    }

                    if ((e.MessageText.ToLower().Contains("qmove")) && (racer.isadmin))
                    {
                        string username = e.MessageText;
                        username = e.MessageText.Substring(username.IndexOf("qmove ") + 6);

                        lfsracer moveQracer = (lfsracer)racerDatabase[getConnectionid(username)];
                        if (moveQracer != null)
                        {
                            if (moveQracer.Status == lfsracer.RacerQStatus.finished)
                                moveQracer.Status = lfsracer.RacerQStatus.spectated;
                            Qqueue.Dequeue();
                            Qqueue.Enqueue(moveQracer);
                            AdminCall(moveQracer.playername + TextColor.White + " moved to end of list");
                            if (Qqueue.Count > 0)
                            {
                                lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                                if (Qualifier != null)
                                {
                                    handler.SendMessageToConnection(Qualifier.playername + TextColor.White + ", Start your qualification run", Qualifier.connectionid);
                                    AdminCall(Qualifier.playername + " is next");
                                }
                            }
                        }
                    }

                    if ((e.MessageText.Contains("qremove".ToLower())) && (racer.isadmin))
                    {
                        Qqueue.Dequeue();
                        if (Qqueue.Count > 0)
                        {
                            lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                            if (Qualifier != null)
                            {
                                handler.SendMessageToConnection(Qualifier.playername + TextColor.White + ", Start your qualification run", Qualifier.connectionid);
                                AdminCall(Qualifier.playername + " is next");
                            }
                        }
                    }

                    if ((e.MessageText.Contains("export")) && (racer.isadmin))
                    {
                        try
                        {
                            StreamWriter wr = new StreamWriter("export.csv");

                            foreach (DictionaryEntry entry in racerDatabase)
                            {
                                lfsracer racer2 = (lfsracer)entry.Value;
                                wr.WriteLine(racer2.playername + "," + racer2.laptime);
                            }
                            wr.Close();
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }

        void handler_RaceTrackConnection(InSimHandler sender, RaceTrackConnection e)
        {
            if (e.ConnectionId != 0)
            {
                if (!racerDatabase.ContainsKey(e.ConnectionId))
                {
                    lfsracer racer = new lfsracer(e.UserName, e.ConnectionId);
                    racer.playername = e.PlayerName;
                    racer.isadmin = e.IsAdmin;
                    racer.isremote = e.IsRemote;
                    racer.Status = lfsracer.RacerQStatus.spectated;
                    racerDatabase.Add(e.ConnectionId, racer);
                    handler.SendMessageToConnection("Type $help at any time to view the help file of " + ProgramTitle, racer.connectionid);
                }
            }
        }

        //void handler_RaceTrackConnectionLeave(InSimHandler sender, RaceTrackConnectionLeave e)
        //{
        //    if (racerDatabase.ContainsKey(e.ConnectionId))
        //    {
        //        lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];
        //        if (racer != null)
        //        {
        //            if (Qqueue.Count > 0)
        //            {
        //                lfsracer nextQ = (lfsracer)Qqueue.Peek();
        //                if (nextQ != null)
        //                {
        //                    if (racer.username == nextQ.username)
        //                    {
        //                        Qqueue.Dequeue();// Remove the player
        //                        // and invite the next one!
        //                        if (Qqueue.Count > 0)
        //                        {
        //                            lfsracer secondracer = (lfsracer)Qqueue.Peek();
        //                            if (secondracer != null)
        //                            {
        //                                handler.SendMessageToConnection("You are next!", secondracer.connectionid);
        //                                AdminCall(secondracer.playername + " is next");
        //                            }
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //        // Finally remove it
        //        racerDatabase.Remove(e.ConnectionId);
        //    }
        //}

        void handler_RaceTrackPlayer(InSimHandler sender, RaceTrackPlayer e)
        {
            if (racerDatabase.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)racerDatabase[e.ConnectionId];
                racer.plate = e.Plate;
                racer.playerid = e.PlayerId;
                racer.isracing = true;

                if (racer.Status == lfsracer.RacerQStatus.finished)
                {
                    handler.SendMessageToConnection("You've already completed your qualifying run.", racer.connectionid);
                    AdminCall(racer.playername + TextColor.White + " tried to join after already qualified");
                    handler.SendMessage("/spec " + racer.username);
                }
                else
                {
                    if (Qqueue.Count > 0)
                    {
                        lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                        if (racer.username.Equals(Qualifier.username))
                        {
                            // We got the first one in the Queue :)
                            racer.Status = lfsracer.RacerQStatus.outlap;
                            AdminCall(racer.playername + TextColor.White + " status : " + racer.Status);
                        }
                        else
                        {
                            handler.SendMessageToConnection(TextColor.White + "It is not your turn yet, please wait to be called", racer.connectionid);
                            AdminCall(racer.playername + TextColor.White + " tried to join before their turn");
                            AdminCall(Qualifier.playername + TextColor.White + " needs to start their run first");
                            handler.SendMessage("/spec " + racer.username);
                        }
                    }
                }
            }
        }

        void handler_RaceTrackPlayerLap(InSimHandler sender, RaceTrackPlayerLap e)
        {
            lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];

            if (racer != null)
            {
                if (e.LapTime < racer.laptime)
                    racer.laptime = e.LapTime;

                switch (racer.Status)
                {
                    case lfsracer.RacerQStatus.outlap:
                        racer.Status = lfsracer.RacerQStatus.qualificationlap;
                        handler.SendMessageToConnection(qualstarttext, racer.connectionid);
                        AdminCall(racer.playername + TextColor.White + " is starting qualifying laps");

                        if (QualifyingMode == 4)
                            SendNewQualifier(racer);

                        break;
                    case lfsracer.RacerQStatus.qualificationlap:
                        if ((e.LapsDone - 1) == QualificationLaps)
                        {
                            racer.Status = lfsracer.RacerQStatus.inlap;
                            handler.SendMessageToConnection(qualendtext, racer.connectionid);
                            AdminCall(racer.playername + TextColor.White + " finished qualifying, should be on inlap");

                            if (QualifyingMode == 5)
                                SendNewQualifier(racer);
                        }
                        else
                        {
                            handler.SendMessageToConnection(Math.Abs((QualificationLaps + 1) - e.LapsDone).ToString() + " laps left", racer.connectionid);
                        }
                        break;
                    case lfsracer.RacerQStatus.inlap:
                        AdminCall(racer.playername + TextColor.White + " made an extra qualifying lap");
                        handler.SendMessage("/spec " + racer.username);
                        racer.Status = lfsracer.RacerQStatus.finished;
                        break;
                    default:
                        break;
                }                
            }
        }

        void handler_RaceTrackPlayerLeave(InSimHandler sender, RaceTrackPlayerLeave e)
        {
            if (racerDatabase.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                racer.isracing = false;
                if (racer.Status == lfsracer.RacerQStatus.inlap)
                {
                    racer.Status = lfsracer.RacerQStatus.finished;
                }
                if ((racer.Status == lfsracer.RacerQStatus.outlap) | (racer.Status == lfsracer.RacerQStatus.qualificationlap))
                {
                    racer.Status = lfsracer.RacerQStatus.finished;
                    handler.SendMessageToConnection(TextColor.White + "You have forfeited your qualification attempt", racer.connectionid);
                    AdminCall(racer.playername + TextColor.White + " has forfeited their qualification attemp");
                }
            }
        }

        void handler_RaceTrackPlayerPits(InSimHandler sender, RaceTrackPlayerPits e)
        {
            if (racerDatabase.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.PlayerId)];
                racer.isracing = false;
                if (racer.Status == lfsracer.RacerQStatus.inlap)
                {
                    racer.Status = lfsracer.RacerQStatus.finished;
                }
                if ((racer.Status == lfsracer.RacerQStatus.outlap) | (racer.Status == lfsracer.RacerQStatus.qualificationlap))
                {
                    racer.Status = lfsracer.RacerQStatus.finished;
                    handler.SendMessageToConnection(TextColor.White + "You have forfeited your qualification attempt", racer.connectionid);
                    AdminCall(racer.playername + TextColor.White + " has forfeited their qualification attemp");
                }
            }
        }

        void handler_RaceTrackReorder(InSimHandler sender, RaceTrackReorder e)
        {
            for (int i = 0; i < e.OrderedPlayerIds.Length; i++)
            {
                lfsracer racer = (lfsracer)racerDatabase[getConnectionid(e.OrderedPlayerIds[i])];
                Qqueue.Enqueue(racer);
            }

            foreach (lfsracer lfsracer in Qqueue)
            {
                handler.SendMessage("/spec " + lfsracer.username);
                //handler.SendMessageToConnection(TextColor.White + "Track cleared in preparation for first racer's qualification attempt.", lfsracer.connectionid);
                //handler.SendMessageToConnection(TextColor.White + "Please wait stay spectated until you are called.", lfsracer.connectionid);
            }
        }

        void handler_RaceTrackRaceStart(InSimHandler sender, RaceTrackRaceStart e)
        {
            handler.SendMessage(TextColor.White + "Qualification Starts! Good luck everyone!");
            handler.SendMessage(TextColor.White + "Use $qlist, to see the qualification order.");
            handler.SendMessage(TextColor.White + "Track cleared in preparation for first racer's qualification attempt.");
            handler.SendMessage(TextColor.White + "You will be called to do your qualification run, don't join until then!");
            if (Qqueue.Count > 0)
            {
                lfsracer Qualifier = (lfsracer)Qqueue.Peek();
                if (Qualifier != null)
                {
                    handler.SendMessageToConnection(Qualifier.playername + TextColor.White + ", Start your qualification run", Qualifier.connectionid);
                    AdminCall(Qualifier.playername + " is next");
                }
            }
        }

        public void Start()
        {
                Assembly SampleAssembly = Assembly.Load(Assembly.GetExecutingAssembly().FullName);
                foreach (Attribute a in SampleAssembly.GetCustomAttributes(true))
                {
                    if (a is AssemblyTitleAttribute)
                    {
                        ProgramTitle = (a as AssemblyTitleAttribute).Title;
                    }
                    if (a is AssemblyFileVersionAttribute)
                    {
                        ProgramVersion = (a as AssemblyFileVersionAttribute).Version;
                    }
                    if (a is AssemblyCompanyAttribute)
                    {
                        ProgramCompany = (a as AssemblyCompanyAttribute).Company;
                    }
                }

                Console.Title = ProgramTitle + " " + ProgramVersion;
                Console.WriteLine(ProgramTitle);
                Console.WriteLine("Version: " + ProgramVersion);
                Console.WriteLine(ProgramCompany);
                Console.WriteLine("Connecting");
                handler.Initialize(10);
                Console.WriteLine("Connected");
                handler.SendMessage("Race Control System Restarting...");
                handler.SendMessage(ProgramTitle);
                handler.SendMessage("Version: " + ProgramVersion);
                handler.SendMessage(ProgramCompany);
                handler.RequestConnectionInfo();
                handler.RequestPlayerInfo();
                handler.RequestMultiplayer();
        }

        public void Threading()
        {
            while (handler.State == InSimHandler.HandlerState.Connected)
            {
                Thread.Sleep(1);
            }
        }

        public byte getConnectionid(byte playerid)
        {
            try
            {
                foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                {
                    lfsracer racer = (lfsracer)Entry.Value;
                    if (racer.playerid == playerid)
                        return racer.connectionid;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return (byte)0;
            }
        }

        public byte getConnectionid(string username)
        {
            try
            {
                foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                {
                    lfsracer racer = (lfsracer)Entry.Value;
                    if (racer.username == username)
                        return racer.connectionid;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return (byte)0;
            }
        }

        public void AdminCall(string message)
        {
            try
            {
                // To make it threadsafe, even when hashtable is synchronised, another thread can access it.
                // You can prevent this, with 'locking' the object. (not sure if an error can occur somewhere else. try/catch ect
                //lock (this.racerDatabase)
                {
                    lock (this.racerDatabase)
                    {
                        foreach (System.Collections.DictionaryEntry Entry in this.racerDatabase)
                        {
                            lfsracer racer = (lfsracer)Entry.Value;
                            if (racer.isadmin)
                            {
                                // Send the message to each admin on the server.
                                handler.SendMessageToConnection(message, racer.connectionid);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}
